//==========================================================================
//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
/*
 * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
 * OF SUCH DAMAGE.
 *
 * This file is part of the lwIP TCP/IP stack.
 *
 * Author: Adam Dunkels <adam@sics.se>
 *
 */

/*
 * This file is a skeleton for developing Ethernet network interface
 * drivers for lwIP. Add code to the low_level functions and do a
 * search-and-replace for the word "ethernetif" to replace it with
 * something that better describes your network interface.
 */
#include "lwip/opt.h"
#include "lwip/def.h"
#include "lwip/mem.h"
#include "lwip/pbuf.h"
#include "lwip/sys.h"
#include "lwip/tcpip.h"
#include <lwip/stats.h>

#include "netif/etharp.h"

/* Define those to better describe your network interface. */
#define IFNAME0 'e'
#define IFNAME1 'n'

#define USER_KILL_DHCP_THREAD  2

u8_t couldSetNetifAttribute = 1;

struct ethernetif
{
    struct eth_addr *ethaddr;
  /* Add whatever per-interface state that is needed here. */
};

static const struct eth_addr ethbroadcast = {{0xff,0xff,0xff,0xff,0xff,0xff}};

/* Forward declarations. */
static void  ethernetif_input(struct netif *netif, unsigned char* databuf, int datalen);
static err_t ethernetif_output(struct netif *netif, struct pbuf *p,
             struct ip_addr *ipaddr);

static void
low_level_init(struct netif *netif)
{
//  struct ethernetif *ethernetif = netif->state;

  /* set MAC hardware address length */
    netif->hwaddr_len = 6;

  /* set MAC hardware address */
//  netif->hwaddr[0] = ;
//  ...
//  netif->hwaddr[5] = ;

  /* maximum transfer unit */
    netif->mtu = 1500;

  /* broadcast capability */
    netif->flags |= NETIF_FLAG_BROADCAST;

  /* Do whatever else is needed to initialize interface. */
}

/*
 * low_level_output():
 *
 * Should do the actual transmission of the packet. The packet is
 * contained in the pbuf that is passed to the function. This pbuf
 * might be chained.
 *
 */
static err_t
low_level_output(struct netif *netif, struct pbuf *p)
{
//  struct ethernetif *ethernetif = netif->state;
    struct pbuf *q;
    char buf[1514];
    int len = 0;

//  initiate transfer();

#if ETH_PAD_SIZE
    pbuf_header(p, -ETH_PAD_SIZE);   /* drop the padding word */
#endif

    for (q = p; q != NULL; q = q->next) {
        /* Send the data from the pbuf to the interface, one pbuf at a
       time. The size of the data in each pbuf is kept in the ->len
       variable. */
//    send data from(q->payload, q->len);
        memcpy(buf+len, q->payload, q->len);
        len += q->len;
    }

//  signal that packet should be sent();
    rtl8139_output(buf, len, netif->pNetDriver);
#if ETH_PAD_SIZE
    pbuf_header(p, ETH_PAD_SIZE);   /* reclaim the padding word */
#endif

#if LINK_STATS
    lwip_stats.link.xmit++;
#endif /* LINK_STATS */

    return ERR_OK;
}

/*
 * low_level_input():
 *
 * Should allocate a pbuf and transfer the bytes of the incoming
 * packet from the interface into the pbuf.
 *
 */

static struct pbuf *
low_level_input(struct netif *netif, unsigned char* databuf, int datalen)
{
//  struct ethernetif *ethernetif = netif->state;
    struct pbuf *p, *q;
    u16_t len;

  /* Obtain the size of the packet and put it into the "len"
     variable. */
    len = datalen;
//    kprintf(" low_in ");
#if ETH_PAD_SIZE
    len += ETH_PAD_SIZE;      /* allow room for Ethernet padding */
#endif

  /* We allocate a pbuf chain of pbufs from the pool. */
    p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);

    if (p != NULL) {

#if ETH_PAD_SIZE
        pbuf_header(p, -ETH_PAD_SIZE);   /* drop the padding word */
#endif

        /* We iterate over the pbuf chain until we have read the entire
     * packet into the pbuf. */
        for (q = p; q != NULL; q = q->next) {
            /* Read enough bytes to fill this pbuf in the chain. The
       * available data in the pbuf is given by the q->len
       * variable. */
//      read data into(q->payload, q->len);
            memcpy(q->payload, databuf, q->len);
            databuf += q->len;
        }
//    acknowledge that packet has been read();

#if ETH_PAD_SIZE
        pbuf_header(p, ETH_PAD_SIZE);   /* reclaim the padding word */
#endif

#if LINK_STATS
        lwip_stats.link.recv++;
#endif /* LINK_STATS */
    }
    else {
//    drop packet();
#if LINK_STATS
        lwip_stats.link.memerr++;
        lwip_stats.link.drop++;
#endif /* LINK_STATS */
    }

    return p;
}

/*
 * ethernetif_output():
 *
 * This function is called by the TCP/IP stack when an IP packet
 * should be sent. It calls the function called low_level_output() to
 * do the actual transmission of the packet.
 *
 */

static err_t
ethernetif_output(struct netif *netif, struct pbuf *p,
    struct ip_addr *ipaddr)
{

 /* resolve hardware address, then send (or queue) packet */
    return etharp_output(netif, ipaddr, p);
}

/*
 * ethernetif_input():
 *
 * This function should be called when a packet is ready to be read
 * from the interface. It uses the function low_level_input() that
 * should handle the actual reception of bytes from the network
 * interface.
 *
 */

static void
ethernetif_input(struct netif *netif, unsigned char*databuf, int datalen)
{
    struct ethernetif *ethernetif;
    struct eth_hdr *ethhdr;
    struct pbuf *p;

    ethernetif = netif->state;

  /* move received packet into a new pbuf */
    p = low_level_input(netif, databuf, datalen);
  /* no packet could be read, silently ignore this */
    if (p == NULL) return;
  /* points to packet payload, which starts with an Ethernet header */
    ethhdr = p->payload;

#if LINK_STATS
    lwip_stats.link.recv++;
#endif /* LINK_STATS */

    ethhdr = p->payload;

    switch (htons(ethhdr->type)) {
  /* IP packet? */
        case ETHTYPE_IP:
            /* update ARP table */
            etharp_ip_input(netif, p);
            /* skip Ethernet header */
            pbuf_header(p, -(s16_t)sizeof(struct eth_hdr));
            if (*(char*)(p->payload) != 0x45) {
                pbuf_free(p);
                p = NULL;
                break;
            }
            /* pass to network layer */
            netif->input(p, netif);
            break;

        case ETHTYPE_ARP:
            /* pass p to ARP module  */
            etharp_arp_input(netif, ethernetif->ethaddr, p);
            break;
        default:
            pbuf_free(p);
            p = NULL;
            break;
    }
}

static void
arp_timer(void *arg)
{
    etharp_tmr();
    sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL);
}

/*
 * ethernetif_init():
 *
 * Should be called at the beginning of the program to set up the
 * network interface. It calls the function low_level_init() to do the
 * actual setup of the hardware.
 *
 */

err_t
ethernetif_init(struct netif *netif)
{
    struct ethernetif *ethernetif;

    ethernetif = mem_malloc(sizeof(struct ethernetif));

    if (ethernetif == NULL) {
        LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_init: out of memory\n"));
        return ERR_MEM;
    }

    netif->state = ethernetif;
    netif->name[0] = IFNAME0;
    netif->name[1] = IFNAME1;
    netif->output = ethernetif_output;
    netif->linkoutput = low_level_output;
    netif->flags |= NETIF_FLAG_UP;

    ethernetif->ethaddr = (struct eth_addr *)&(netif->hwaddr[0]);

    low_level_init(netif);

    etharp_init();

    sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL);
    return ERR_OK;
}

struct sys_timeouts *                                                   //
sys_arch_timeouts(void)                                                 //
{                                                                       //
    return (struct sys_timeouts*)getthreadpointer();                    //
}                                                                       //

void *
extern_etherifnet_init(void* haddr, u32_t ip, u32_t mask, u32_t gateway, u32_t priDns, void *pNetDriver, wchar_t * deviceName, void *pNetThreadExitArg)
{
    struct ip_addr ipaddr, netmask, gw, dns;
    struct netif *pRtl8139if;
    char arrDeviceName[16];
    ipaddr.addr = ip;
    netmask.addr = mask;
    gw.addr = gateway;
    dns.addr = priDns;

    if (NULL == pNetDriver) {
        return NULL;
    }
    pRtl8139if = (struct netif *)malloc(sizeof(struct netif));
    if (NULL == pRtl8139if) {
        return NULL;
    }
    pRtl8139if->pNetThreadExitArg = pNetThreadExitArg;
    memcpy(&(pRtl8139if->hwaddr[0]), haddr, 6);
    pRtl8139if->pNetDriver = pNetDriver;
    pRtl8139if->pNetThreadExitArg = pNetThreadExitArg;

    memset(arrDeviceName, 0, sizeof(arrDeviceName));
    if (NULL != deviceName) {
        if (wcslen(deviceName) > sizeof(arrDeviceName)/sizeof(arrDeviceName[0]) - 1)
            DebugBreak();
        _wcstostr(arrDeviceName, deviceName);
    }
    
    netif_set_default(netif_add(pRtl8139if, arrDeviceName, &ipaddr, &netmask,
            &gw, &dns, NULL, NULL, ethernetif_init, tcpip_input));
    //memset(pRtl8139if->deviceName, 0, sizeof(pRtl8139if->deviceName));
    //wcscpy((wchar_t *)(pRtl8139if->deviceName), deviceName);
    return (void *)pRtl8139if;
}

void
extern_ethernetif_input(unsigned char *databuf, int datalen, void *pNetif)
{
    ethernetif_input((struct netif *)pNetif, databuf, datalen);
}

int
netif_del(wchar_t *ethernetName)
{
    struct netif *pnetif;
    char arrDeviceName[16];

    memset(arrDeviceName, 0, sizeof(arrDeviceName));
    if (wcslen(ethernetName) > sizeof(arrDeviceName)/sizeof(arrDeviceName[0]) - 1)
        DebugBreak();
    _wcstostr(arrDeviceName, ethernetName);

    for (pnetif = netif_list; NULL != pnetif; pnetif = pnetif->next) {
        //if (!wcscmp((wchar_t*)(pnetif->deviceName), ethernetName)) {
        if (!strcmp(arrDeviceName, pnetif->adapter)) {
            netif_remove(pnetif);
            //*pThreadExitArg = pnetif->pNetThreadExitArg;
        #if LWIP_DHCP
            if (NULL != pnetif->dhcpThreadArg) {
                net_thread_kill(pnetif->dhcpThreadArg);
                while (USER_KILL_DHCP_THREAD != get_thread_exit_flag(pnetif->dhcpThreadArg)) {
                    LwipSleep(3);
                }
                free(pnetif->dhcpThreadArg);
                pnetif->dhcpThreadArg = NULL;
            }
        #endif
            net_thread_kill(pnetif->pNetThreadExitArg);
            free(pnetif->pNetThreadExitArg);
            free(pnetif);
            return 1;
        }
    }
    return 0;
}

void 
extern_etherifnet_changeIp(wchar_t *ethernetName, void* haddr, u32_t ip, u32_t mask, u32_t gateway, u32_t priDns, u32_t secdDns)
{
    struct netif *pnetif;
    char arrDeviceName[16];
    struct ip_addr ipaddr, netmask, gw, dns1,dns2;

    memset(arrDeviceName, 0, sizeof(arrDeviceName));
    if (wcslen(ethernetName) > sizeof(arrDeviceName)/sizeof(arrDeviceName[0]) - 1)
        DebugBreak();

    _wcstostr(arrDeviceName, ethernetName);

    for (pnetif = netif_list; NULL != pnetif; pnetif = pnetif->next) {
        if (!strcmp(arrDeviceName, pnetif->adapter)) {
            ipaddr.addr = ip;
            netmask.addr = mask;
            gw.addr = gateway;
            dns1.addr = priDns;
            dns2.addr = secdDns;
            memcpy(&(pnetif->hwaddr[0]), haddr, 6);
            netif_set_addr(pnetif, &ipaddr, &netmask, &gw, &dns1, &dns2);
            return ;
        }
    }
}

s32_t
Lwip_SetNetifAttribute(void *pnetif, u32_t ip, u32_t mask, u32_t gw, u32_t firstdns, u32_t seconddns)
{
    struct netif *plnetif;
    struct ip_addr ipaddr, netmask, gateway, dns1, dns2;
    if (!couldSetNetifAttribute) {
        kprintf("Net block has started, you can't set param now!\n");
        return (s32_t)ERR_OPT;
    }
    if (NULL == pnetif) {
        return (s32_t)ERR_ARG;
    }
    if (ip == INADDR_NONE || mask == INADDR_NONE || gw == INADDR_NONE || firstdns == INADDR_NONE || seconddns == INADDR_NONE) {
        return (s32_t)ERR_ARG;
    }

    for (plnetif = netif_list; NULL != plnetif; plnetif = plnetif->next) {
        if (pnetif == plnetif) {
            if (0 != ip)
                ipaddr.addr = ip;
            if (0 != mask)
                netmask.addr = mask;
            if (0 != gw)
                gateway.addr = gw;
            if (0 != firstdns)
                dns1.addr = firstdns;
            if (0 != seconddns)
                dns2.addr = seconddns;

            netif_set_addr(plnetif, &ipaddr, &netmask, &gateway, &dns1, &dns2);
            return (s32_t)ERR_OK;
        }
    }
    return (s32_t)ERR_IF;
}


#if LWIP_DHCP
void
set_dhcp_thread_exit_arg(void *pNetif, void *pExitArg)
{
    if (NULL == pNetif || NULL == pExitArg) {
        return;
    }
    ((struct netif *)pNetif)->dhcpThreadArg = pExitArg;
}
int DHCP_Client(void *parg)
{
    struct netif *pnetif;
    struct ip_addr ipaddr, netmask, getway, dnsaddr;
    void *pThreadExitArg;
    u32_t mscnt = 0;

    if (NULL == parg)
        return -1;
    pnetif = (struct netif *)parg;
    pThreadExitArg = pnetif->dhcpThreadArg;

    ipaddr.addr = 0;
    netmask.addr = 0;
    getway.addr = 0;
    dnsaddr.addr = 0;
    netif_set_addr(pnetif, &ipaddr, &netmask, &getway, &dnsaddr, NULL);

    if (ERR_OK != dhcp_start(pnetif)) {
        kprintf("*ERROR* DHCP client start failed!!\n");
        return -1;
    }

    if (DHCP_BOUND == (pnetif->dhcp)->state) 
        return 0;

    while (DHCP_BOUND != (pnetif->dhcp)->state && 0 == get_thread_exit_flag(pThreadExitArg)) {
        LwipSleep(DHCP_FINE_TIMER_MSECS);//DHCP_FINE_TIMER_MSECS
        dhcp_fine_tmr();
        mscnt += DHCP_FINE_TIMER_MSECS;
        if (mscnt >= DHCP_COARSE_TIMER_SECS * 1000) {
            dhcp_coarse_tmr();
            mscnt = 0;
        }
    }
    if (DHCP_BOUND == (pnetif->dhcp)->state) {
        kprintf("DHCP client get ip address success!\n");
        kprintf("Client Ip: %s\n", inet_ntoa(*((struct in_addr *)&(pnetif->ip_addr))));
        kprintf("NetMask: %s\n", inet_ntoa(*((struct in_addr *)&(pnetif->netmask))));
        kprintf("GetWay: %s\n", inet_ntoa(*((struct in_addr *)&(pnetif->gw))));
        kprintf("DNS1 Address: %s\n", inet_ntoa(*((struct in_addr *)&(pnetif->dns1))));
        kprintf("DNS2 Address: %s\n", inet_ntoa(*((struct in_addr *)&(pnetif->dns2))));
    }
    //if (1 == get_thread_exit_flag(pThreadExitArg)) {
    //    kprintf("user Kill the dhcp thread!\n");
    //}

    if (0 == get_thread_exit_flag(pThreadExitArg) 
          && DHCP_BOUND == (pnetif->dhcp)->state) {
        //DHCP client end normally, so we free the dhcpThreadArg
        dhcp_stop(pnetif);
        free(pnetif->dhcpThreadArg);
        pnetif->dhcpThreadArg = NULL;
        return 0;
    }
    dhcp_stop(pnetif);
    set_thread_exit_flag(pThreadExitArg, USER_KILL_DHCP_THREAD);
    return 0;
}
#endif

